#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <errno.h>
#include <string.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h> // for close
#define RECVBUFSIZE 2048
void *rec_func(void *arg)
{
 int listenfd,new_fd,nbytes;
 char buffer[RECVBUFSIZE];
 int i;
struct sockaddr_in client_addr;
struct sockaddr_in server_addr;
 int sin_size;
 sin_size=sizeof(struct sockaddr_in);
 new_fd = *((int *) arg);
 free(arg); 
 
 while(1)
 {
  if((nbytes=recv(new_fd,buffer, RECVBUFSIZE,0))==-1)//接收后用来回传,从客户端接收
  {
   fprintf(stderr,"Read Error:%s\n",strerror(errno));
   exit(1);
  }
  if(nbytes == -1)
  {//客户端出错了 返回值-1
   close(new_fd);
   break;   
  }
  if(nbytes == 0)
  {//客户端主动断开连接，返回值是0
   close(new_fd);
   break;
  }
  buffer[nbytes]='\0'; 
  getsockname(new_fd,(struct sockaddr *)&server_addr,&sin_size);
  getpeername(new_fd,(struct sockaddr *)&client_addr,&sin_size);
  printf("[client%s:%d to server%s:%d]client have sent:%s\n",inet_ntoa(client_addr.sin_addr),htons(client_addr.sin_port),inet_ntoa(server_addr.sin_addr),htons(server_addr.sin_port),buffer); 
  
  if(send(new_fd,buffer,strlen(buffer),0)==-1)//回传给客户端
  {
   fprintf(stderr,"Write Error:%s\n",strerror(errno));
   exit(1);
  }
   
 }

}

int main(int argc, char *argv[])
{
 char buffer[RECVBUFSIZE];
 int listenfd,new_fd,nbytes;
 struct sockaddr_in server_addr;
 struct sockaddr_in serverlisten_addr;
 struct sockaddr_in servernew_addr;
 struct sockaddr_in client_addr;
 int sin_size,portnumber;
 char hello[]="Hello! Socket communication world!\n";
 pthread_t tid;
 int *pconnsocke = NULL;
 int ret,i;
 int on = 1;
 /*  格式不对，退出 */
 if(argc!=2)
 {
  fprintf(stderr,"Usage:[%s] portnumber is in wrong format\a\n",argv[0]);
  exit(1);
 }
 /*端口号不对，退出*/
 if((portnumber=atoi(argv[1]))<0)
 {
  fprintf(stderr,"Usage:[%s] portnumber [%s] is less than 0\a\n",argv[0],argv[1]);
  exit(1);
 }

 /*服务器创建监听套接口描述符listenfd（ip协议族+tcp套接口类型）  listenfd用于监听,一般一个服务器只有一个*/
 if((listenfd=socket(AF_INET,SOCK_STREAM,0))==-1)  
 {
  fprintf(stderr,"Socket error:%s\n\a",strerror(errno));
  exit(1);
 }
 if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(int)) < 0)//SO_REUSEADDR端口重用,防止上次强制退出后端口号没释放
 {
    perror("setsockopet error\n");
    return -1;
 }
 /*服务器端填充 sockaddr结构*/ 
 bzero(&server_addr,sizeof(struct sockaddr_in));
 server_addr.sin_family     =AF_INET;//ipv4协议族
 /*自动填充主机IP*/
 server_addr.sin_addr.s_addr=htonl(INADDR_ANY);//自动获取网卡地址,动态 IP
 server_addr.sin_port       =htons(portnumber);//配置端口信息
 
 /*绑定端口号和地址到listenfd描述符*/ 
 if(bind(listenfd,(struct sockaddr *)(&server_addr),sizeof(struct sockaddr))==-1)
 {
  fprintf(stderr,"Bind error:%s\n\a",strerror(errno));
  exit(1);
 }
 
 /*启动监听listenfd描述符      server:CLOSED->LISTEN  */
 if(listen(listenfd, 10)==-1)//最大排队个数为10,已连接(3次握手)最多数量
 {
  fprintf(stderr,"Listen error:%s\n\a",strerror(errno));
  exit(1);
 }
  // printf("server_addr: %s:%d\n",inet_ntoa(server_addr.sin_addr),htons(server_addr.sin_port));
  // getsockname(listenfd,(struct sockaddr *)&serverlisten_addr,&sin_size);
  // printf("serverlisten_addr: %s:%d\n",inet_ntoa(serverlisten_addr.sin_addr),htons(serverlisten_addr.sin_port));
 while(1)
 {
  /*服务器阻塞，直到新的客户程序建立连接*/
  sin_size=sizeof(struct sockaddr_in);
//已连接(3次握手)套接口new_fd，server为本地，client为对端
  printf("ready to connect\n"); 
  if((new_fd = accept(listenfd,(struct sockaddr *)&client_addr,&sin_size))==-1)//每次有新连接都会产生新描述符,否则阻塞在此
  {//new_fd本地为server,对端为client,accept获得对端客户端地址servernew_addr
    fprintf(stderr,"Accept error:%s\n\a",strerror(errno));
    exit(1);
  }
  printf("connect from: %s:%d\n",inet_ntoa(client_addr.sin_addr),htons(client_addr.sin_port)); 
  getsockname(new_fd,(struct sockaddr *)&servernew_addr,&sin_size);//获得客户端指定的服务器ip地址
  printf("set server address: %s:%d\n",inet_ntoa(servernew_addr.sin_addr),htons(servernew_addr.sin_port)); 
  pconnsocke = (int *) malloc(sizeof(int));
  *pconnsocke = new_fd;
  
  ret = pthread_create(&tid, NULL, rec_func, (void *) pconnsocke);
  if (ret < 0) 
  {
   perror("pthread_create err");
   return -1;
  } 
 }
 close(listenfd);
 exit(0);
}
